package com.mh.framework.security.filter;

import com.mh.system.domain.LoginUserDetails;
import com.mh.system.service.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;

//Token过滤器: 验证Token的有效性
@Component
public class TokenAuthFilter extends OncePerRequestFilter {

    @Autowired
    private TokenService tokenService;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        //1. redis中获取用户信息-->调用TokenService
        LoginUserDetails loginUser = tokenService.getLoginUser(request);

        //2. 获取当前的安全上下文SecurityContext中的认证信息Authentication
        /** 如果用户已经通过认证, auth非空; 否则为空
         *  auth非空, 表示之前已经认证通过, 不走下面的if, 直接放行 --> 不走当前类的过滤器
         *  auth为空, 表示没认证过, 需要走if --> 要走当前类的过滤器
         */
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();

        //3. 如果用户信息存在且当前上下文中没有认证信息
        if (!Objects.isNull(loginUser) && Objects.isNull(auth)) {

            //4. 验证Token的有效期,有效期少于20分钟重新更新过期时间&用户信息
            tokenService.verifyToken(loginUser);

            //5.1  创建一个新的认证对象
            UsernamePasswordAuthenticationToken newAuth = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
            //5.2 将与当前请求相关的详细信息(如远程IP地址、SessionID等)设置到 Authentication 对象中
            newAuth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            //5.3 将认证信息设置到安全上下文中, 以便后续的授权和认证逻辑使用
            SecurityContextHolder.getContext().setAuthentication(newAuth);
        }
        filterChain.doFilter(request, response);
    }
}
